/*
 *  This license is based on the BSD license adopted by the Apache Foundation.
 */
package newpeer;

import ejercicios.HelloWorld;
import net.jxta.discovery.DiscoveryEvent;
import net.jxta.discovery.DiscoveryListener;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.id.ID;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.platform.NetworkManager;
import net.jxta.protocol.DiscoveryResponseMsg;

import java.awt.BorderLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import net.jxta.id.IDFactory;
import net.jxta.pipe.PipeID;
import net.jxta.pipe.PipeService;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaSocket;
import org.farng.mp3.MP3File;
import org.farng.mp3.TagException;
import org.farng.mp3.id3.ID3v1;

/**
 * Illustrates the use of Discovery Service
 * Note this is for illustration purposes and is not meant as a blue-print
 */
public class Peer extends JFrame implements DiscoveryListener {
    //    public static final int PUERTO = 12345;

    public static final int PUERTO = 12345;
    public static final long TAM_PAQUETES = 512;
    private static final String SEPARADOR_MENSAJE = ";";
    private String peerName;
    private PanelBotonesAcciones panelBotonesAcciones;
    private JLabel estadoConexion;
    private transient NetworkManager manager;
    private transient DiscoveryService discovery;
    private transient PeerGroup netPeerGroup = null;
    private PanelArchivos panelArchivos;
    private transient PipeAdvertisement pipeAdv;
    private final static long ITERATIONS = 1000;
    private final static int PAYLOADSIZE = 64 * 1024;
    private PipeID advPipeId;
    private ID idpeer;
    private String criterio;
    public final static String SOCKETIDSTR = "urn:jxta:uuid-59616261646162614E5047205032503393B5C2F6CA7A41FBB0F890173088E79404";
    //Lista asociativa en la que la llave el el nombre de archivo y el valor es su advertisement asociado
    private HashMap<String, PipeAdvertisementArchivo> arlist;
    private DialogoBusquedaArchivos instanciaDialogo;
    private String atribuye;
    private Long tamanocriterio;
    private String[] contenidos;
    private HashMap<String, PipeAdvertisementArchivo> listaArchivosRemotos;

    /**
     * Constructor for the Peer
     */
    public Peer(String peerName) {

        this.arlist = new HashMap<String, PipeAdvertisementArchivo>();
        this.peerName = peerName;
        this.setTitle("Documentos Compartidos");
        this.setSize(400, 400);
        this.setLayout(new BorderLayout());

        try {
            AdvertisementFactory.registerAdvertisementInstance(PipeAdvertisementArchivo.getAdvertisementType(), new PipeAdvertisementArchivo.Instantiator());
            manager = new NetworkManager(NetworkManager.ConfigMode.ADHOC, peerName,
                    new File(new File(".cache"), peerName).toURI());
            manager.startNetwork();
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
        netPeerGroup = manager.getNetPeerGroup();
        System.out.println("Id grupo peer"+ netPeerGroup.getPeerGroupID());
     
        try {
            PeerServer p = new PeerServer(netPeerGroup);
            Thread threaduno = new Thread(p);
            threaduno.start();
            Thread.currentThread().setName(SocketServer.class.getName() + ".main()");
        } catch (Throwable e) {
            System.err.println("Failed : " + e);
            e.printStackTrace(System.err);
            System.exit(-1);
        }
        // get the discovery service
        discovery = netPeerGroup.getDiscoveryService();
        discovery.addDiscoveryListener(this);
        inicializarArchivosRemotos();

        pipeAdv = createSocketAdvertisement();



    }

    /**
     * mainer
     *
     * @param args command line args
     */
    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
        String NamePeer = JOptionPane.showInputDialog("Por favor ingrese el nombre del Peer");
        Peer discoveryServer = new Peer(NamePeer);
        discoveryServer.setVisible(true);
        discoveryServer.start();
    }

    /**
     * create a new pipe adv, publish it for 2 minut network time,
     * sleep for 3 minutes, then repeat
     */
    public void start() {
        //creacion de Un hilo dedicado a la publicacion de Advertisements en la RED
        ThreadPublicacionArchivosCompartidos t = new ThreadPublicacionArchivosCompartidos(discovery, this.peerName);
        t.start();
    }

    public void buscarArchivos(String criterio, String Atributo, DialogoBusquedaArchivos dial, String[] Contenido) throws IOException {
        long waittime = 5 * 1000L;
        atribuye = Atributo;
        this.instanciaDialogo = dial;
        this.criterio = criterio;
        this.contenidos = Contenido;
        arlist.clear();
        try {
            System.out.println("Sending a Discovery Message");
            System.out.println("El criterio de busqueda es: " + criterio);
            System.out.println("Buscando por: " + Atributo);

            // look for any peer
            if (atribuye.equals("X Extension")) {
                tamanocriterio = Long.parseLong(criterio);
                discovery.getRemoteAdvertisements(
                        // no specific peer (propagate)
                        null,
                        // Adv type
                        DiscoveryService.ADV,
                        // Attribute = name
                        "name",
                        // Value = the tutorial
                        "Archivo X",
                        // 1000 advertisement response is all we are looking for
                        1000,
                        // no query specific listener. we are using a global listener
                        null);
            } else if (atribuye.equals("X Contenido")) {
                JOptionPane.showMessageDialog(null,"BUscando por contenido");
                discovery.getRemoteAdvertisements(
                        // no specific peer (propagate)
                        null,
                        // Adv type
                        DiscoveryService.ADV,
                        // Attribute = name
                        "name",
                        // Value = the tutorial
                        "Archivo X",
                        // 1000 advertisement response is all we are looking for
                        1000,
                        // no query specific listener. we are using a global listener
                        null);
            } //(Atributo.equals("X Archivo"))
            else {
                discovery.getRemoteAdvertisements(
                        // no specific peer (propagate)
                        null,
                        // Adv type
                        DiscoveryService.ADV,
                        // Attribute = name
                        "nombreArchivo",
                        // Value = the tutorial
                        criterio,
                        // 1000 advertisement response is all we are looking for
                        1000,
                        // no query specific listener. we are using a global listener
                        null);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void discoveryEvent(DiscoveryEvent ev) {

        DiscoveryResponseMsg res = ev.getResponse();
        // let's get the responding peer's advertisement
        System.out.println("DISCOVERY_RECIBE [  Got a Discovery Response [" + res.getResponseCount() + " elements]  from peer : " + ev.getSource() + "  ]");

        PipeAdvertisementArchivo adv;
        Enumeration en = res.getAdvertisements();
        if (en != null) {
            while (en.hasMoreElements()) {
                adv = (PipeAdvertisementArchivo) en.nextElement();
                System.out.println(" Peer Information = " + adv.toString());

                if (!arlist.containsKey(adv.getOSName())) {
                    arlist.put(adv.getOSName(), adv);
                }
                idpeer = adv.getID();
                System.out.println(" El ID del archivo del peer= " + idpeer);
                System.out.println("" + adv.getAno());
            }
            if (atribuye.equals("ArchivosRemotos")) {
                listaArchivosRemotos = arlist;
                finalizarConstruccionInterfaz();
            }
            if (atribuye.equals("X Extension")) {
                arlist = darArchivosEnRango(arlist, tamanocriterio);
            }
            if (atribuye.equals("X Contenido")) {
                JOptionPane.showMessageDialog(null,"Llenando lista por contenido");
                arlist = darArchivosCoincidentesBusquedaContenido(arlist, "mp3", contenidos[0], contenidos[1], contenidos[2], contenidos[3]);
            }
            if (instanciaDialogo != null) {
                instanciaDialogo.actualizarListaArchivos(arlist);
            }
        }
    }

    public void descargarArchivo(String nombreArchivo, String tamanoArchivo) {
        //  String mensaje = "DESCARGAR_ARCHIVO" + SEPARADOR_MENSAJE + nombreArchivo;
        try {
            JxtaSocket socketServidor = conectarse();
            OutputStream clienteOut = socketServidor.getOutputStream();
            InputStream clienteIn = socketServidor.getInputStream();
            PrintWriter pw = new PrintWriter(clienteOut, true);
            pw.println("DESCARGAR_ARCHIVO;" + nombreArchivo);
            long tamArchivo = Long.parseLong(tamanoArchivo);
            try {
                File nuevoArchivo = new File("../temp/cliente/" + nombreArchivo);
                if (!nuevoArchivo.getParentFile().exists()) {
                    //Si no existen las carpetas crearlas
                    nuevoArchivo.getParentFile().mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(nuevoArchivo);
                long tamPaquetes = TAM_PAQUETES;
                long numeroPaquetes = tamArchivo / tamPaquetes;
                if (tamArchivo < tamPaquetes) {
                    byte[] menor = new byte[(int) tamArchivo];
                    clienteIn.read(menor);
                    fos.write(menor);
                } else {
                    for (int i = 0; i < numeroPaquetes; i++) {
                        byte[] paq = new byte[(int) tamPaquetes];
                        clienteIn.read(paq);
                        fos.write(paq);
                    }
                    long loQueFalta = tamArchivo - (numeroPaquetes * tamPaquetes);
                    if (loQueFalta > 0) {
                        byte[] paq = new byte[(int) loQueFalta];
                        clienteIn.read(paq);
                        fos.write(paq);
                    }
                }
                fos.close();
                desconectarse(socketServidor);
                JOptionPane.showMessageDialog(this, "El archivo ha sido descargado exitosamente", "Carga Archivo", JOptionPane.INFORMATION_MESSAGE);
                this.panelArchivos.abrirArchivo(nuevoArchivo.getAbsolutePath());
                //Actualizar Interfaz Gráfica
                this.panelArchivos.actualizarArchivos();
                super.repaint();
                pack();

            } catch (FileNotFoundException e) {
                JOptionPane.showMessageDialog(this, "Archivo no encontrado", "Carga Archivo", JOptionPane.ERROR_MESSAGE);
                e.printStackTrace();
                //desconectarDeServidor();
            }
        } catch (IOException ex) {
            Logger.getLogger(Peer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void compartirArchivo(File archivoACompartir) throws IOException {
        //Llevar el nuevo Archivo a ../temp
        File nuevoArchivo = new File("../temp/" + archivoACompartir.getName());
        InputStream clienteIn = new FileInputStream(archivoACompartir);
        if (!nuevoArchivo.getParentFile().exists()) {
            //Si no existen las carpetas crearlas
            nuevoArchivo.getParentFile().mkdirs();
        }
        long tamArchivo = archivoACompartir.length();
        FileOutputStream fos = new FileOutputStream(nuevoArchivo);
        long tamPaquetes = TAM_PAQUETES;
        long numeroPaquetes = tamArchivo / tamPaquetes;
        if (tamArchivo < tamPaquetes) {
            byte[] menor = new byte[(int) tamArchivo];
            clienteIn.read(menor);
            fos.write(menor);
        } else {
            for (int i = 0; i < numeroPaquetes; i++) {
                byte[] paq = new byte[(int) tamPaquetes];
                clienteIn.read(paq);
                fos.write(paq);
            }
            long loQueFalta = tamArchivo - (numeroPaquetes * tamPaquetes);
            if (loQueFalta > 0) {
                byte[] paq = new byte[(int) loQueFalta];
                clienteIn.read(paq);
                fos.write(paq);
            }
        }
        fos.close();
        // Actualizar Interfaz Gráfica
        this.panelArchivos.actualizarArchivos();
        super.repaint();
        pack();
    }

    /**
     * Stops the platform
     */
    public void stop() {
        // Stop JXTA
        manager.stopNetwork();
    }

    public HashMap<String, PipeAdvertisementArchivo> darArchivosRemotos() {
        return listaArchivosRemotos;
    }

    private JxtaSocket conectarse() throws IOException {
        long start = System.currentTimeMillis();
        System.out.println("Connecting to the network");
        // JxtaSocket socket = new JxtaSocket();
        JxtaSocket socket = new JxtaSocket(netPeerGroup,
                // no specific peerid
                null,
                pipeAdv,
                // connection timeout: 5 seconds
                6000,
                // reliable connection
                true);

        // get the socket output stream
        return socket;
    }

    private void desconectarse(JxtaSocket socketServidor) {
        try {
            socketServidor.close();
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(this, "Error al desconectar del Servidor", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    public void mostrarInfoMP3(String rutaArchivo) {

        if (rutaArchivo.endsWith(".mp3")) {
            try {
                File arch = new File(rutaArchivo);
                MP3File archivoMp3 = new MP3File(arch);
                ID3v1 tagInfo = archivoMp3.getID3v1Tag();
                String albumCancion = tagInfo.getAlbumTitle();
                String artistaCancion = tagInfo.getArtist();
                String nombreCancion = tagInfo.getSongTitle();
                String anoCancion = tagInfo.getYear();
                System.out.println("INFO: " + albumCancion + "\n" + artistaCancion + "\n" + nombreCancion + "\n" + anoCancion + "\n");
            } catch (IOException ex) {
                Logger.getLogger(HelloWorld.class.getName()).log(Level.SEVERE, null, ex);
            } catch (TagException ex) {
                Logger.getLogger(HelloWorld.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public PipeAdvertisement createSocketAdvertisement() {
        PipeID socketID = null;

        try {
            socketID = (PipeID) IDFactory.fromURI(new URI(SOCKETIDSTR));
        } catch (URISyntaxException use) {
            use.printStackTrace();
        }
        PipeAdvertisement advertisement = (PipeAdvertisement) AdvertisementFactory.newAdvertisement(PipeAdvertisement.getAdvertisementType());

        advertisement.setPipeID(socketID);
        advertisement.setType(PipeService.UnicastType);
        advertisement.setName("Peer to peer socket");
        System.out.println("Saliendo del socket server");
        return advertisement;
    }

    public HashMap<String, PipeAdvertisementArchivo> darArchivosEnRango(HashMap<String, PipeAdvertisementArchivo> hashAdvertisementsArchivos, Long tamArchivo) {
        //Comparar los tamaños de los archivos 2MB
        HashMap<String, PipeAdvertisementArchivo> listaArchivosCoincidentes = new HashMap<String, PipeAdvertisementArchivo>();
        Collection<PipeAdvertisementArchivo> advertisementsArchivos = hashAdvertisementsArchivos.values();
        for (PipeAdvertisementArchivo advertisementArchivo : advertisementsArchivos) {
            Long tamArchivoAProbar = Long.parseLong(advertisementArchivo.getOStamano());
            int rangoComparacion = 1024 * 1024 * 2;
            Long limInferior = tamArchivoAProbar - rangoComparacion;
            Long limSuperior = tamArchivoAProbar + rangoComparacion;
            if (tamArchivo > limInferior && tamArchivo < limSuperior) {
                listaArchivosCoincidentes.put(advertisementArchivo.getOSName(), advertisementArchivo);
            }
        }
        return listaArchivosCoincidentes;
    }

    public HashMap<String, PipeAdvertisementArchivo> darArchivosCoincidentesBusquedaContenido(HashMap<String, PipeAdvertisementArchivo> hashAdvertisementsArchivos,
            String formatoArchivo, String nombreArtista, String nombreCancion, String nombreAlbum, String anoAlbum) {

        Collection<PipeAdvertisementArchivo> advertisementsArchivos = hashAdvertisementsArchivos.values();
        HashMap<String, PipeAdvertisementArchivo> listaArchivosCoincidentes = new HashMap<String, PipeAdvertisementArchivo>();
        for (PipeAdvertisementArchivo advertisementArchivo : advertisementsArchivos) {
            String nombreArchivo = advertisementArchivo.getOSName();
            String nombreArtistaAdvertisement = advertisementArchivo.getNombreArtista(),
                    nombreCancionAdvertisement = advertisementArchivo.getNombreCancion(),
                    nombreAlbumAdvertisement = advertisementArchivo.getNombreAlbum(),
                    anoAlbumAdvertisement = advertisementArchivo.getAno();
            if (nombreArchivo.toLowerCase().endsWith(formatoArchivo)) {
                if (nombreArtista.equalsIgnoreCase(nombreArtistaAdvertisement) || nombreCancion.equalsIgnoreCase(nombreCancionAdvertisement) || nombreAlbum.equals(nombreAlbumAdvertisement) || anoAlbum.equals(anoAlbumAdvertisement)) {
                    listaArchivosCoincidentes.put(advertisementArchivo.getOSName(), advertisementArchivo);
                    JOptionPane.showMessageDialog(null, "Agrega "+advertisementArchivo.getOSName() +advertisementArchivo.getAno());

                }
            }
        }
        return listaArchivosCoincidentes;
    }

    private void finalizarConstruccionInterfaz() {
        this.panelBotonesAcciones = new PanelBotonesAcciones(this);
        this.panelArchivos = new PanelArchivos(this);
        //conectarConServidor();
        this.add(this.panelArchivos, BorderLayout.CENTER);
        this.add(this.panelBotonesAcciones, BorderLayout.SOUTH);
        this.setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
    }

    private void inicializarArchivosRemotos() {
        atribuye = "ArchivosRemotos";
        discovery.getRemoteAdvertisements(
                // no specific peer (propagate)
                null,
                // Adv type
                DiscoveryService.ADV,
                // Attribute = name
                "name",
                // Value = the tutorial
                "Archivo X",
                // 1000 advertisement response is all we are looking for
                1000,
                // no query specific listener. we are using a global listener
                null);
    }
}

